#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
数据库 CRUD 操作

这个模块包含所有数据库的增删改查操作：
1. 用户 CRUD 操作
2. 物品 CRUD 操作
3. 通用 CRUD 基类

使用 SQLAlchemy ORM 进行数据库操作
提供类型安全和异常处理

作者: FastAPI 学习项目
创建时间: 2024
"""

from typing import List, Optional, Dict, Any, Type, TypeVar, Generic
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, desc, asc, func
from pydantic import BaseModel
from loguru import logger

from app.models.user import User, UserCreate, UserUpdate
from app.models.item import Item, ItemCreate, ItemUpdate, ItemSearch
from app.utils.security import get_password_hash, verify_password

# 泛型类型变量
ModelType = TypeVar("ModelType")
CreateSchemaType = TypeVar("CreateSchemaType", bound=BaseModel)
UpdateSchemaType = TypeVar("UpdateSchemaType", bound=BaseModel)


class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]):
    """
    CRUD 操作基类
    
    提供通用的增删改查操作，可以被具体的 CRUD 类继承
    
    Args:
        ModelType: SQLAlchemy 模型类型
        CreateSchemaType: 创建数据的 Pydantic 模型类型
        UpdateSchemaType: 更新数据的 Pydantic 模型类型
    """
    
    def __init__(self, model: Type[ModelType]):
        """
        初始化 CRUD 基类
        
        Args:
            model: SQLAlchemy 模型类
        """
        self.model = model
    
    def get(self, db: Session, id: int) -> Optional[ModelType]:
        """
        根据 ID 获取单个记录
        
        Args:
            db: 数据库会话
            id: 记录 ID
            
        Returns:
            Optional[ModelType]: 找到的记录或 None
        """
        try:
            return db.query(self.model).filter(self.model.id == id).first()
        except Exception as e:
            logger.error(f"获取记录失败 (ID: {id}): {e}")
            return None
    
    def get_multi(
        self, 
        db: Session, 
        *, 
        skip: int = 0, 
        limit: int = 100
    ) -> List[ModelType]:
        """
        获取多个记录
        
        Args:
            db: 数据库会话
            skip: 跳过的记录数
            limit: 限制返回的记录数
            
        Returns:
            List[ModelType]: 记录列表
        """
        try:
            return db.query(self.model).offset(skip).limit(limit).all()
        except Exception as e:
            logger.error(f"获取记录列表失败: {e}")
            return []
    
    def create(self, db: Session, *, obj_in: CreateSchemaType) -> ModelType:
        """
        创建新记录
        
        Args:
            db: 数据库会话
            obj_in: 创建数据的 Pydantic 模型
            
        Returns:
            ModelType: 创建的记录
            
        Raises:
            Exception: 创建失败时抛出异常
        """
        try:
            obj_in_data = obj_in.dict()
            db_obj = self.model(**obj_in_data)
            db.add(db_obj)
            db.commit()
            db.refresh(db_obj)
            logger.info(f"创建记录成功: {self.model.__name__} ID={db_obj.id}")
            return db_obj
        except Exception as e:
            db.rollback()
            logger.error(f"创建记录失败: {e}")
            raise
    
    def update(
        self,
        db: Session,
        *,
        db_obj: ModelType,
        obj_in: UpdateSchemaType
    ) -> ModelType:
        """
        更新记录
        
        Args:
            db: 数据库会话
            db_obj: 要更新的数据库对象
            obj_in: 更新数据的 Pydantic 模型
            
        Returns:
            ModelType: 更新后的记录
            
        Raises:
            Exception: 更新失败时抛出异常
        """
        try:
            obj_data = obj_in.dict(exclude_unset=True)
            for field, value in obj_data.items():
                setattr(db_obj, field, value)
            db.add(db_obj)
            db.commit()
            db.refresh(db_obj)
            logger.info(f"更新记录成功: {self.model.__name__} ID={db_obj.id}")
            return db_obj
        except Exception as e:
            db.rollback()
            logger.error(f"更新记录失败: {e}")
            raise
    
    def remove(self, db: Session, *, id: int) -> Optional[ModelType]:
        """
        删除记录
        
        Args:
            db: 数据库会话
            id: 要删除的记录 ID
            
        Returns:
            Optional[ModelType]: 被删除的记录或 None
            
        Raises:
            Exception: 删除失败时抛出异常
        """
        try:
            obj = db.query(self.model).get(id)
            if obj:
                db.delete(obj)
                db.commit()
                logger.info(f"删除记录成功: {self.model.__name__} ID={id}")
                return obj
            return None
        except Exception as e:
            db.rollback()
            logger.error(f"删除记录失败: {e}")
            raise
    
    def count(self, db: Session) -> int:
        """
        获取记录总数
        
        Args:
            db: 数据库会话
            
        Returns:
            int: 记录总数
        """
        try:
            return db.query(self.model).count()
        except Exception as e:
            logger.error(f"获取记录总数失败: {e}")
            return 0


class CRUDUser(CRUDBase[User, UserCreate, UserUpdate]):
    """
    用户 CRUD 操作类
    
    继承基础 CRUD 类，提供用户特定的操作
    """
    
    def get_by_email(self, db: Session, *, email: str) -> Optional[User]:
        """
        根据邮箱获取用户
        
        Args:
            db: 数据库会话
            email: 用户邮箱
            
        Returns:
            Optional[User]: 找到的用户或 None
        """
        try:
            return db.query(User).filter(User.email == email).first()
        except Exception as e:
            logger.error(f"根据邮箱获取用户失败 ({email}): {e}")
            return None
    
    def get_by_username(self, db: Session, *, username: str) -> Optional[User]:
        """
        根据用户名获取用户
        
        Args:
            db: 数据库会话
            username: 用户名
            
        Returns:
            Optional[User]: 找到的用户或 None
        """
        try:
            return db.query(User).filter(User.username == username).first()
        except Exception as e:
            logger.error(f"根据用户名获取用户失败 ({username}): {e}")
            return None
    
    def create(self, db: Session, *, obj_in: UserCreate) -> User:
        """
        创建新用户
        
        重写基类方法，添加密码加密
        
        Args:
            db: 数据库会话
            obj_in: 用户创建数据
            
        Returns:
            User: 创建的用户
            
        Raises:
            Exception: 创建失败时抛出异常
        """
        try:
            # 检查用户名和邮箱是否已存在
            if self.get_by_username(db, username=obj_in.username):
                raise ValueError(f"用户名 '{obj_in.username}' 已存在")
            
            if self.get_by_email(db, email=obj_in.email):
                raise ValueError(f"邮箱 '{obj_in.email}' 已存在")
            
            # 创建用户数据
            create_data = obj_in.dict(exclude={"password", "confirm_password"})
            create_data["hashed_password"] = get_password_hash(obj_in.password)
            
            db_obj = User(**create_data)
            db.add(db_obj)
            db.commit()
            db.refresh(db_obj)
            
            logger.info(f"创建用户成功: {db_obj.username} ({db_obj.email})")
            return db_obj
            
        except Exception as e:
            db.rollback()
            logger.error(f"创建用户失败: {e}")
            raise
    
    def authenticate(self, db: Session, *, username: str, password: str) -> Optional[User]:
        """
        用户认证
        
        Args:
            db: 数据库会话
            username: 用户名或邮箱
            password: 密码
            
        Returns:
            Optional[User]: 认证成功的用户或 None
        """
        try:
            # 尝试用户名登录
            user = self.get_by_username(db, username=username)
            if not user:
                # 尝试邮箱登录
                user = self.get_by_email(db, email=username)
            
            if not user:
                logger.warning(f"用户不存在: {username}")
                return None
            
            if not user.is_active:
                logger.warning(f"用户未激活: {username}")
                return None
            
            if not verify_password(password, user.hashed_password):
                logger.warning(f"密码错误: {username}")
                return None
            
            logger.info(f"用户认证成功: {user.username}")
            return user
            
        except Exception as e:
            logger.error(f"用户认证失败: {e}")
            return None
    
    def update_password(self, db: Session, *, user: User, new_password: str) -> User:
        """
        更新用户密码
        
        Args:
            db: 数据库会话
            user: 用户对象
            new_password: 新密码
            
        Returns:
            User: 更新后的用户
            
        Raises:
            Exception: 更新失败时抛出异常
        """
        try:
            user.hashed_password = get_password_hash(new_password)
            db.add(user)
            db.commit()
            db.refresh(user)
            logger.info(f"用户密码更新成功: {user.username}")
            return user
        except Exception as e:
            db.rollback()
            logger.error(f"更新用户密码失败: {e}")
            raise
    
    def get_active_users(self, db: Session, *, skip: int = 0, limit: int = 100) -> List[User]:
        """
        获取活跃用户列表
        
        Args:
            db: 数据库会话
            skip: 跳过的记录数
            limit: 限制返回的记录数
            
        Returns:
            List[User]: 活跃用户列表
        """
        try:
            return (
                db.query(User)
                .filter(User.is_active == True)
                .offset(skip)
                .limit(limit)
                .all()
            )
        except Exception as e:
            logger.error(f"获取活跃用户列表失败: {e}")
            return []
    
    def search_users(self, db: Session, *, keyword: str, skip: int = 0, limit: int = 100) -> List[User]:
        """
        搜索用户
        
        Args:
            db: 数据库会话
            keyword: 搜索关键词
            skip: 跳过的记录数
            limit: 限制返回的记录数
            
        Returns:
            List[User]: 搜索结果
        """
        try:
            return (
                db.query(User)
                .filter(
                    or_(
                        User.username.contains(keyword),
                        User.full_name.contains(keyword),
                        User.email.contains(keyword)
                    )
                )
                .offset(skip)
                .limit(limit)
                .all()
            )
        except Exception as e:
            logger.error(f"搜索用户失败: {e}")
            return []


class CRUDItem(CRUDBase[Item, ItemCreate, ItemUpdate]):
    """
    物品 CRUD 操作类
    
    继承基础 CRUD 类，提供物品特定的操作
    """
    
    def create_with_owner(self, db: Session, *, obj_in: ItemCreate, owner_id: int) -> Item:
        """
        创建物品（指定所有者）
        
        Args:
            db: 数据库会话
            obj_in: 物品创建数据
            owner_id: 所有者 ID
            
        Returns:
            Item: 创建的物品
            
        Raises:
            Exception: 创建失败时抛出异常
        """
        try:
            create_data = obj_in.dict()
            create_data["owner_id"] = owner_id
            
            # 处理标签
            if "tags" in create_data and create_data["tags"]:
                create_data["tags"] = ",".join(create_data["tags"])
            
            db_obj = Item(**create_data)
            db.add(db_obj)
            db.commit()
            db.refresh(db_obj)
            
            logger.info(f"创建物品成功: {db_obj.title} (所有者: {owner_id})")
            return db_obj
            
        except Exception as e:
            db.rollback()
            logger.error(f"创建物品失败: {e}")
            raise
    
    def get_by_owner(self, db: Session, *, owner_id: int, skip: int = 0, limit: int = 100) -> List[Item]:
        """
        获取用户的物品列表
        
        Args:
            db: 数据库会话
            owner_id: 所有者 ID
            skip: 跳过的记录数
            limit: 限制返回的记录数
            
        Returns:
            List[Item]: 物品列表
        """
        try:
            return (
                db.query(Item)
                .filter(Item.owner_id == owner_id)
                .offset(skip)
                .limit(limit)
                .all()
            )
        except Exception as e:
            logger.error(f"获取用户物品列表失败: {e}")
            return []
    
    def get_available_items(self, db: Session, *, skip: int = 0, limit: int = 100) -> List[Item]:
        """
        获取可用物品列表
        
        Args:
            db: 数据库会话
            skip: 跳过的记录数
            limit: 限制返回的记录数
            
        Returns:
            List[Item]: 可用物品列表
        """
        try:
            return (
                db.query(Item)
                .filter(Item.is_available == True)
                .offset(skip)
                .limit(limit)
                .all()
            )
        except Exception as e:
            logger.error(f"获取可用物品列表失败: {e}")
            return []
    
    def search_items(self, db: Session, *, search_params: ItemSearch, skip: int = 0, limit: int = 100) -> List[Item]:
        """
        搜索物品
        
        Args:
            db: 数据库会话
            search_params: 搜索参数
            skip: 跳过的记录数
            limit: 限制返回的记录数
            
        Returns:
            List[Item]: 搜索结果
        """
        try:
            query = db.query(Item)
            
            # 关键词搜索
            if search_params.keyword:
                query = query.filter(
                    or_(
                        Item.title.contains(search_params.keyword),
                        Item.description.contains(search_params.keyword)
                    )
                )
            
            # 分类过滤
            if search_params.category:
                query = query.filter(Item.category == search_params.category)
            
            # 价格范围过滤
            if search_params.min_price is not None:
                query = query.filter(Item.price >= search_params.min_price)
            
            if search_params.max_price is not None:
                query = query.filter(Item.price <= search_params.max_price)
            
            # 标签过滤
            if search_params.tags:
                for tag in search_params.tags:
                    query = query.filter(Item.tags.contains(tag))
            
            # 可用性过滤
            if search_params.is_available is not None:
                query = query.filter(Item.is_available == search_params.is_available)
            
            # 所有者过滤
            if search_params.owner_id is not None:
                query = query.filter(Item.owner_id == search_params.owner_id)
            
            return query.offset(skip).limit(limit).all()
            
        except Exception as e:
            logger.error(f"搜索物品失败: {e}")
            return []
    
    def get_categories(self, db: Session) -> List[Dict[str, Any]]:
        """
        获取所有分类及其物品数量
        
        Args:
            db: 数据库会话
            
        Returns:
            List[Dict[str, Any]]: 分类统计列表
        """
        try:
            result = (
                db.query(Item.category, func.count(Item.id).label('count'))
                .filter(Item.category.isnot(None))
                .group_by(Item.category)
                .all()
            )
            
            return [
                {"category": category, "count": count}
                for category, count in result
            ]
            
        except Exception as e:
            logger.error(f"获取分类统计失败: {e}")
            return []
    
    def get_popular_tags(self, db: Session, limit: int = 10) -> List[Dict[str, Any]]:
        """
        获取热门标签
        
        Args:
            db: 数据库会话
            limit: 返回的标签数量限制
            
        Returns:
            List[Dict[str, Any]]: 热门标签列表
        """
        try:
            # 获取所有标签
            items = db.query(Item.tags).filter(Item.tags.isnot(None)).all()
            
            # 统计标签频率
            tag_count = {}
            for item in items:
                if item.tags:
                    tags = [tag.strip() for tag in item.tags.split(",") if tag.strip()]
                    for tag in tags:
                        tag_count[tag] = tag_count.get(tag, 0) + 1
            
            # 排序并返回前N个
            sorted_tags = sorted(tag_count.items(), key=lambda x: x[1], reverse=True)
            
            return [
                {"tag": tag, "count": count}
                for tag, count in sorted_tags[:limit]
            ]
            
        except Exception as e:
            logger.error(f"获取热门标签失败: {e}")
            return []


# 创建 CRUD 实例
crud_user = CRUDUser(User)
crud_item = CRUDItem(Item)


# 导出所有组件
__all__ = [
    "CRUDBase",
    "CRUDUser",
    "CRUDItem",
    "crud_user",
    "crud_item"
]